#ifndef __win_impl_h__
#define __win_impl_h__

#pragma warning(disable:4311)

#include "windefine.h"
#include "ui_handler.h"
#include "winpainter.h"
#include "hwnd_delegate.h"
#include "thread_manager.h"
#include "animate_loop.h"
#include "../base/msg_pump.h"


typedef CWinTraits<WS_POPUP | WS_VISIBLE | WS_CLIPCHILDREN | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX, WS_EX_LAYERED>    WinImplTraits;

template <typename T, typename TWinTraits = WinImplTraits>
class WinImpl
    : public CWindowImpl<T, CWindow, TWinTraits>
    , public ITaskDelegate
{
public:
    DECLARE_WND_CLASS(_T("WinImpl"))

    WinImpl()
        : alpha_( 255 )
    {
    }

    ~WinImpl()
    {
    }

    BOOL Parse( LPCTSTR lpszFile )
    {
        if( !lpszFile )
        {
            return FALSE;
        }

        ui_handler_.SetPath( lpszFile );
        if( ui_handler_.Parse() )
        {
            ui_handler_.SetWindowDelegate( &window_delegate_ );
            return TRUE;
        }

        return FALSE;
    }

    void SetWindowAlpha( BYTE alpha )
    {
        alpha_ = alpha;
    }

    BOOL RegisterMessageObserver( const std::string &id, core::IMessageObserver * observer )
    {
        if( id.size() <= 0 || !observer )
        {
            return FALSE;
        }

        core::FaceWindow * control = ui_handler_.FindControl( id );
        if( !control )
        {
            return FALSE;
        }

        control->AddMessageObserver( observer );
        return TRUE;
    }

    void DrawLayeredWindow( HDC hDC, RECT rcUpdate )
    {
        RECT rcWnd;
        ::GetClientRect( m_hWnd, &rcWnd );
        int width = rcWnd.right - rcWnd.left;
        int height = rcWnd.bottom - rcWnd.top;

        HDC hMemDC = ::CreateCompatibleDC( hDC );
        HBITMAP hBitmap = (HBITMAP)::SelectObject( hMemDC, (HBITMAP)win_image_cache_ );
        ::FillRect( hMemDC, &rcUpdate, (HBRUSH)::GetStockObject( BLACK_BRUSH ) );

        UpdateRect( hMemDC, rcUpdate );

        POINT ptDst = { rcWnd.left, rcWnd.top };
        ::ClientToScreen( m_hWnd, &ptDst );
        POINT ptSrc = { 0, 0 };
        SIZE  szWnd = { width, height };

        BLENDFUNCTION blend;
        blend.BlendOp = AC_SRC_OVER;
        blend.BlendFlags = 0;
        blend.SourceConstantAlpha = (BYTE)alpha_;
        blend.AlphaFormat = AC_SRC_ALPHA;
        ::UpdateLayeredWindow( m_hWnd, hDC, &ptDst, &szWnd, hMemDC, &ptSrc, 0, &blend, ULW_ALPHA );

        ::SelectObject( hMemDC, hBitmap );
        ::DeleteDC( hMemDC );
    }

public:
    BEGIN_MSG_MAP(WinImpl)
        MESSAGE_HANDLER( WM_CREATE, OnCreate )
        MESSAGE_HANDLER( WM_SIZE, OnSize )
        MESSAGE_HANDLER( WM_PAINT, OnPaint )
        MESSAGE_HANDLER( WM_ERASEBKGND, OnEraseBkgnd )
        MESSAGE_HANDLER( WM_LBUTTONDOWN, OnLButtonDown )
        MESSAGE_HANDLER( WM_LBUTTONUP, OnLButtonUp )
        MESSAGE_HANDLER( WM_MOUSEMOVE, OnMouseMove )
    END_MSG_MAP()

    HRESULT OnCreate( UINT, WPARAM, LPARAM, BOOL )
    {
        window_delegate_.Attatch( m_hWnd );
        return TRUE;
    }

    HRESULT OnSize( UINT, WPARAM wParam, LPARAM lParam, BOOL )
    {
        switch( wParam )
        {
        case SIZE_MINIMIZED:
        case SIZE_MAXHIDE:
            return TRUE;

        case SIZE_MAXIMIZED:
        case SIZE_MAXSHOW:
        case SIZE_RESTORED:
            break;
        }

        int width = LOWORD( lParam );
        int height = HIWORD( lParam );
        if( width == 0 || height == 0 )
        {
            return TRUE;
        }

        if( (HBITMAP)win_image_cache_ )
        {
            win_image_cache_.DeleteObject();
        }

        win_image_cache_.CreateDIBSection( width, height );
        core::FaceWindow *body = ui_handler_.Body();
        if( body )
        {
            core::rect rcWnd;
            rcWnd.set( 0, 0, width, height );

            core::layout lyout;
            lyout.leaf = false;
            lyout.content = rcWnd;
            lyout.width = width;
            lyout.height = height;

            body->DispatchMessage( core::FM_LAYOUT, 0, (core::uint)&lyout );
        }

        HDC hDC = ::GetDC( m_hWnd );
        RECT rcUpdate = { 0, 0, width, height };
        DrawLayeredWindow( hDC, rcUpdate );
        ::ReleaseDC( m_hWnd, hDC );

        return TRUE;
    }

    HRESULT OnPaint( UINT, WPARAM, LPARAM, BOOL )
    {
        RECT rcUpdate;
        ::GetUpdateRect( m_hWnd, &rcUpdate, FALSE );

        PAINTSTRUCT ps;
        HDC hDC = ::BeginPaint( m_hWnd, &ps );

        DrawLayeredWindow( hDC, rcUpdate );

        ::EndPaint( m_hWnd, &ps );

        return TRUE;
    }

    HRESULT OnEraseBkgnd( UINT, WPARAM, LPARAM, BOOL )
    {
        return TRUE;
    }

    HRESULT OnLButtonDown( UINT, WPARAM wParam, LPARAM lParam, BOOL )
    {
        core::FaceWindow * active = ui_handler_.Active();
        if( active )
        {
            active->DispatchMessage( core::FM_LBUTTONDOWN, (core::uint)wParam, (core::uint)lParam );
        }

        return TRUE;
    }

    HRESULT OnLButtonUp( UINT, WPARAM wParam, LPARAM lParam, BOOL )
    {
        core::FaceWindow * active = ui_handler_.Active();
        if( active )
        {
            active->DispatchMessage( core::FM_LBUTTONUP, (core::uint)wParam, (core::uint)lParam );
        }

        return TRUE;
    }

    HRESULT OnMouseMove( UINT, WPARAM wParam, LPARAM lParam, BOOL )
    {
        int x = GET_X_LPARAM( lParam );
        int y = GET_Y_LPARAM( lParam );
        core::FaceWindow * active = ui_handler_.Active();
        core::FaceWindow * body = ui_handler_.Body();
        core::FaceWindow * control = 0;
        if( active )
        {
            control = active->HitTest( x, y );
        }

        if( !control && body )
        {
            control = body->HitTest( x, y );
        }

        if( control && control != active )
        {
            control->DispatchMessage( core::FM_MOUSEHOVER, (core::uint)wParam, (core::uint)lParam );
            if( active )
            {
                active->DispatchMessage( core::FM_MOUSELEAVE, (core::uint)wParam, (core::uint)lParam );
            }

            ui_handler_.SetActive( control );
        }

        return TRUE;
    }

public:
    virtual void OnDeglegate( DWORD nTask, ITaskHandler * pTaskHandler, HANDLE hEvent )
    {
        switch( nTask )
        {
        case TASK_SET_ATTRIBUTE:
        case TASK_ANIMATE_SET_ATTRIBUTE:
            if( pTaskHandler )
            {
                AttributeHandler * pAttributeTask = (AttributeHandler *)pTaskHandler;
                SetAttributeMainThread( pAttributeTask->Id(), pAttributeTask->Name(), pAttributeTask->Value() );
                pAttributeTask->Drop();
            }
            break;
        case TASK_ANIMATE:
            if( pTaskHandler )
            {
                AnimateHandler * pHandler = (AnimateHandler *)pTaskHandler;
                pHandler->SetDelegate( this );
                pHandler->Handle( hEvent );
                pHandler->Drop();
            }
            break;
        default:
            break;
        }
    }

public:
    BOOL SetAttribute
                    (
                      const std::string &id,
                      const std::string &name,
                      const std::string &value
                     )
    {
        if( id.empty() || name.empty() )
        {
            return FALSE;
        }

        if( ThreadManager::Instance().MessageQueue()->IsCurrentThread( ::GetCurrentThreadId() ) )
        {
            return SetAttributeMainThread( id, name, value );
        }

        AttributeHandler * pHandler = new AttributeHandler( id, name, value );
        if( !pHandler )
        {
            return FALSE;
        }

        return ThreadManager::Instance().AnimateQueue()->PostQueueTask( this, TASK_SET_ATTRIBUTE, pHandler );
    }

    BOOL PutSingleAnimate( float fElapse, float fTotal, Animate *animate )
    {
        SingleAnimateHandler *pHandler = new SingleAnimateHandler( fElapse, fTotal, animate );
        if( !pHandler )
        {
            return FALSE;
        }

        return ThreadManager::Instance().AnimateQueue()->PostQueueTask( this, TASK_ANIMATE, pHandler );
    }

    BOOL PutComplexAnimate( ComplexAnimateHandler * pHandler )
    {
        if( pHandler )
        {
            return ThreadManager::Instance().AnimateQueue()->PostQueueTask( this, TASK_ANIMATE, pHandler );
        }

        return FALSE;
    }

protected:
    BOOL SetAttributeMainThread( const std::string &id, const std::string &name, const std::string &value )
    {
        if( id.empty() || name.empty() )
        {
            return FALSE;
        }

        core::FaceWindow *control = ui_handler_.FindControl( id );
        if( !control )
        {
            return FALSE;
        }

        core::uint lResult = 0;
        if( !control->SetAttribute( name.c_str(), value.c_str(), lResult ) )
        {
            return FALSE;
        }

        if( (lResult & core::ATTRIBUTE_TRAIT_LAYOUT) == core::ATTRIBUTE_TRAIT_LAYOUT )
        {
            core::rect update;
            control->UpdateLayout( update );
            control->UpdateRect( update );
        }

        if( (lResult & core::ATTRIBUTE_TRAIT_PAINT) == core::ATTRIBUTE_TRAIT_PAINT )
        {
            core::rect update = control->GetBorderRect();
            control->UpdateRect( update );
        }

        return TRUE;
    }

    BOOL UpdateRect( HDC hDC, RECT rcUpdate )
    {
        core::FaceWindow *body = ui_handler_.Body();
        if( !body )
        {
            return FALSE;
        }

        core::rect rect;
        rect.set( rcUpdate.left, rcUpdate.top, rcUpdate.right, rcUpdate.bottom );

        HRGN rgnUpdate = ::CreateRectRgn( rcUpdate.left, rcUpdate.top, rcUpdate.right, rcUpdate.bottom );
        if( rgnUpdate )
        {
            ::SelectClipRgn( hDC, rgnUpdate );
        }

        FacePainter painter( (core::canvas)hDC );
        body->DispatchMessage( core::FM_PAINT, (core::uint)&painter, (core::uint)&rect );

        if( rgnUpdate )
        {
            ::DeleteObject( rgnUpdate );
        }

        return TRUE;
    }

protected:
    int             alpha_;
    WindowDelegate  window_delegate_;
    UIHandler       ui_handler_;
    WinImage        win_image_cache_;
};

#endif